﻿/**
 * Copyright (C) 2012-2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Internal.Util;
using System.Reflection;
using NGuice.Inject.Spi;
using NGuice.Inject.Util;

namespace NGuice.Inject.Internal
{
    //完成！
    /// <summary>
    /// 创建具有Provides元注释方法的绑定。使用scope和binding注释在provider方法上以配置bingding
    /// </summary>
    public sealed class ProviderMethodsModule : IModule
    {
        private readonly object delegatee;
        private readonly Type typeLiteral;

        private ProviderMethodsModule(object delegatee)
        {
            this.delegatee = Preconditions.CheckNotNull(delegatee, "delegatee");
            this.typeLiteral = delegatee.GetType();
        }

        /// <summary>
        /// 从给定模块中为提供者方法返回创建绑定的模块
        /// </summary>
        /// <param name="module"></param>
        /// <returns></returns>
        public static IModule ForModule(IModule module)
        {
            return ForObject(module);
        }

        /// <summary>
        /// 从给定对象中为提供者方法返回创建绑定的模块
        /// This is useful notably for <a href="http://code.google.com/p/google-gin/">GIN</a>
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static IModule ForObject(object obj)
        {
            // avoid infinite recursion, since installing a module always installs itself
            if (obj is ProviderMethodsModule)
            {
                return Modules.EMPTY_MODULE;
            }

            return new ProviderMethodsModule(obj);
        }


        public void Configure(IBinder binder)
        {
            foreach (ProviderMethod providerMethod in GetProviderMethods(binder))
            {
                providerMethod.Configure(binder);
            }
        }

        public IList<ProviderMethod> GetProviderMethods(IBinder binder)
        {
            IList<ProviderMethod> result = Lists.NewArrayList<ProviderMethod>();
            for (Type c = delegatee.GetType(); c != typeof(object); c = c.BaseType)
            {
                foreach (MethodInfo method in c.GetMethods())
                {

                    if (method.GetCustomAttributes(typeof(ProvidesAttribute), false).Length > 0)
                    {
                        result.Add(CreateProviderMethod(binder, method));
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 非泛型的方法
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="method"></param>
        /// <returns></returns>
        ProviderMethod CreateProviderMethod(IBinder binder, MethodInfo method)
        {
            binder = binder.WithSource(method);
            Errors errors = new Errors(method);

            // prepare the parameter providers
            List<Dependency> dependencies = Lists.NewArrayList<Dependency>();
            List<IProvider> parameterProviders = Lists.NewArrayList<IProvider>();
            List<Type> parameterTypes = method.GetParameters().Map((p) =>
            {
                return p.ParameterType;
            }).ToList<Type>();
            Attribute[][] parameterAnnotations = method.GetParameterAttributes();
            for (int i = 0; i < parameterTypes.Count; i++)
            {
                Key key = GetKey(errors, parameterTypes[i], method, parameterAnnotations[i]);
                if (key.Equals(Key<Logger>.Get()))
                {
                    // If it was a Logger, change the key to be unique & bind it to a
                    // provider that provides a logger with a proper name.
                    // This solves issue 482 (returning a new anonymous logger on every call exhausts memory)
                    Key<NLog.Logger> loggerKey = Key<NLog.Logger>.Get(UniqueAnnotations.Create());
                    binder.Bind(loggerKey).ToProvider(new LogProvider(method));
                    key = loggerKey;
                }
                dependencies.Add(Dependency<object>.GetForNoGeneric(key));
                parameterProviders.Add(binder.GetProvider(key));
            }

            // Define T as the method's return type.
            Type returnType = method.ReturnType;

            Key k = GetKey(errors, returnType, method, (Attribute[])method.GetCustomAttributes(false));
            Type scopeAnnotation = Attributes.FindScopeAttribute(errors, method.GetCustomAttributes(false));

            foreach (Message message in errors.Messages)
            {
                binder.AddError(message);
            }
            //非泛型方法，只能通过反射调用泛型构造函数：
            //kk, method, delegatee, ImmutableSet<Dependency>.CopyOf<Dependency, Dependency>(dependencies),
            //parameterProviders, scopeAnnotation
            return (ProviderMethod)typeof(ProviderMethod<>).MakeGenericType(k.GetType().GetGenericArguments())
                .GetConstructor(new Type[] { k.GetType(), typeof(MethodInfo), typeof(ImmutableSet<Dependency>), typeof(List<IProvider>), typeof(Type)})
                .Invoke(new object[] { k, method, delegatee, ImmutableSet<Dependency>.CopyOf<Dependency, Dependency>(dependencies), parameterProviders, scopeAnnotation });
        }

        ProviderMethod<T> CreateProviderMethod<T>(IBinder binder, MethodInfo method)
        {
            binder = binder.WithSource(method);
            Errors errors = new Errors(method);

            // prepare the parameter providers
            List<Dependency> dependencies = Lists.NewArrayList<Dependency>();
            List<IProvider> parameterProviders = Lists.NewArrayList<IProvider>();
            List<Type> parameterTypes = method.GetParameters().Map((p) =>
            {
                return p.ParameterType;
            }).ToList<Type>();
            Attribute[][] parameterAnnotations = method.GetParameterAttributes();
            for (int i = 0; i < parameterTypes.Count; i++)
            {
                Key key = GetKey(errors, parameterTypes[i], method, parameterAnnotations[i]);
                if (key.Equals(Key<Logger>.Get()))
                {
                    // If it was a Logger, change the key to be unique & bind it to a
                    // provider that provides a logger with a proper name.
                    // This solves issue 482 (returning a new anonymous logger on every call exhausts memory)
                    Key<NLog.Logger> loggerKey = Key<NLog.Logger>.Get(UniqueAnnotations.Create());
                    binder.Bind(loggerKey).ToProvider(new LogProvider(method));
                    key = loggerKey;
                }
                dependencies.Add(Dependency<object>.GetForNoGeneric(key));
                parameterProviders.Add(binder.GetProvider(key));
            }

            // Define T as the method's return type.
            Type returnType = method.ReturnType;

            Key k = GetKey(errors, returnType, method, (Attribute[])method.GetCustomAttributes(false));
            Type scopeAnnotation = Attributes.FindScopeAttribute(errors, method.GetCustomAttributes(false));

            foreach (Message message in errors.Messages)
            {
                binder.AddError(message);
            }
            //TODO:原本通过反射调用泛型构造函数，现在使用动态对象方式调用会更好，但需要经过测试是否可行!
            dynamic kk = k;
            return new ProviderMethod<T>(kk, method, delegatee, ImmutableSet<Dependency>.CopyOf<Dependency, Dependency>(dependencies),
                parameterProviders, scopeAnnotation);
        }

        /// <summary>
        /// 返回的Key泛型参数为type类型
        /// </summary>
        /// <param name="errors"></param>
        /// <param name="type"></param>
        /// <param name="member"></param>
        /// <param name="annotations"></param>
        /// <returns></returns>
        Key GetKey(Errors errors, Type type, MemberInfo member, Attribute[] annotations)
        {
            Attribute bindingAnnotation = Attributes.FindBindingAnnotation(errors, member, annotations);
            //TODO:通过反射调用Key<>.Get静态方法
            MethodInfo method = typeof(Key<>).MakeGenericType(type).GetMethod("Get", BindingFlags.Static);
            return bindingAnnotation == null ? (Key)method.Invoke(null, new object[0]) : (Key)method.Invoke(null, new object[] { bindingAnnotation });
        }
    }

    /// <summary>
    /// 基于方法名返回一个日志的提供者
    /// </summary>
    internal sealed class LogProvider : IProvider<NLog.Logger>
    {
        private readonly string name;

        public LogProvider(MethodInfo method)
        {
            this.name = method.DeclaringType.Name + "." + method.Name;
        }

        NLog.Logger IProvider<NLog.Logger>.Get()
        {
            return NLog.LogManager.GetLogger(name);
        }

        object IProvider.Get()
        {
            return ((IProvider<NLog.Logger>)this).Get();
        }
    }
}
